package com.gobang.common.filter;

import com.gobang.service.AuthService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.ArrayList;

/**
 * JWT认证过滤器
 * 拦截HTTP请求并验证JWT Token，设置Spring Security上下文
 * 
 * @author System
 * @since 1.0.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JWTAuthenticationFilter extends OncePerRequestFilter {

    private final AuthService authService;

    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_PREFIX = "Bearer ";

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        
        String requestURI = request.getRequestURI();
        log.debug("JWT认证过滤器处理请求：{}", requestURI);

        try {
            // 1. 从请求头获取Token
            String token = extractToken(request);
            
            if (token != null) {
                // 2. 验证Token
                if (authService.validateToken(token)) {
                    // 3. 获取用户ID
                    Long userId = authService.getUserIdFromToken(token);
                    
                    if (userId != null) {
                        // 4. 设置Spring Security认证上下文
                        // 为了跳过用户名/密码的验证：改写用户名/密码认证为已认证状态
                        // 将credentials清空
                        UsernamePasswordAuthenticationToken authentication = 
                            new UsernamePasswordAuthenticationToken(userId, null, new ArrayList<>());
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                        
                        log.debug("JWT认证成功，用户ID：{}，请求URI：{}", userId, requestURI);
                    } else {
                        log.debug("从Token中获取用户ID失败，请求URI：{}", requestURI);
                    }
                } else {
                    log.debug("Token验证失败，请求URI：{}", requestURI);
                }
            } else {
                log.debug("请求中未找到Token，请求URI：{}", requestURI);
            }
            
        } catch (Exception e) {
            log.error("JWT认证过滤器处理异常，请求URI：{}，错误：{}", requestURI, e.getMessage(), e);
            // 认证失败时清除上下文
            SecurityContextHolder.clearContext();

            // 就算在这里抛了异常，也不会被GlobalExceptionHandler去处理。
            // GlobalExceptionHandler是从Controller开始，
            // 但当前Filter的执行，还未到Controller，因此有了以上说明。
        }

        // 继续过滤器链
        filterChain.doFilter(request, response);
    }

    /**
     * 从请求中提取JWT Token
     *
     * @param request HTTP请求
     * @return JWT Token，如果不存在返回null
     */
    private String extractToken(HttpServletRequest request) {
        // 1. 从Authorization头获取
        String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER);
        if (authorizationHeader != null && authorizationHeader.startsWith(BEARER_PREFIX)) {
            return authorizationHeader.substring(BEARER_PREFIX.length());
        }

        // 2. 从请求参数获取（用于WebSocket连接等场景）
        String tokenParam = request.getParameter("token");
        if (tokenParam != null && !tokenParam.trim().isEmpty()) {
            return tokenParam;
        }

        return null;
    }

    /**
     * 判断是否需要跳过认证的请求
     * 对于公开接口，不需要进行JWT认证
     *
     * @param request HTTP请求
     * @return 是否跳过认证
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        
        // 跳过公开接口
        String[] publicPaths = {
            "/api/auth/login",
            "/api/auth/register", 
            "/api/user/register",
            "/test/**",
            "/actuator/**",
            "/index.html",
            "/static/**",
            "/css/**",
            "/js/**",
            "/images/**",
            "/webjars/**",
            "/favicon.ico",
            "/error"
        };
        
        for (String path : publicPaths) {
            if (requestURI.startsWith(path.replace("**", "")) || 
                (path.contains("**") && requestURI.matches(path.replace("**", ".*")))) {
                log.debug("跳过JWT认证，公开接口：{}", requestURI);
                return true;
            }
        }
        
        return false;
    }
} 